package org.udr.query;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

import org.udr.datamodel.Association;
import org.udr.datamodel.Attribute;
import org.udr.datamodel.Entity;
import org.udr.datamodel.Property;

public class QueryBuilder {

	private Query query;
	private final Entity rootEntity;
	private final List<Entity> processedEntities = new ArrayList<Entity>();
	
	private final QueryTreeNode rootNode;
	
	public QueryBuilder (Entity rootEntity){
		this.rootEntity = rootEntity;
		this.rootNode = new QueryTreeNode(rootEntity);
		this.query = new Query(rootEntity.getName());
		processNode(rootNode,rootEntity); 
	}

	private void processNode(QueryTreeNode node, Entity entity) {
		processedEntities.add(entity);
		for (Attribute attribute: entity.getAttributes()) {
			if (attribute instanceof Property) {
				QueryTreeNode propertyNode = new QueryTreeNode(node, (Property) attribute);
				node.addChild(propertyNode);
			} else if (attribute instanceof Association) {
				Association association = (Association) attribute;
				if (!processedEntities.contains(association.getTargetEntity())) {
					QueryTreeNode associationNode = new QueryTreeNode(node, association);
					node.addChild(associationNode);
					processNode(associationNode,association.getTargetEntity() );
				}
			}
			
		}
	}

	public QueryTreeNode getRootEntityNode() {
		return rootNode;
	}	
	
	public Projection addProjection(QueryTreeNode node){
		populateJoins(node.getParent());
		Projection projection = new Projection(node.getParent().getAlias(),node.getName());
		query.addProjection(projection);
		return projection;
	}
	
	public void removeProjection(Projection projection) {
		query.removeProjection(projection);
	}
	
	private void populateJoins(QueryTreeNode node){
		if (!node.equals(rootNode)){
			if (node.getQueryElement() != null ) {
				Join join = new Join(node.getPath(),node.getAlias());
				query.addJoin(join);
				node.setQueryElement(join);
			}
			populateJoins(node.getParent());
		}
	}
	
}

